// Copyright (c) .NET Core Community. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Threading.Tasks;
using DotNetFramework.CAP.Infrastructure;
using DotNetFramework.CAP.Messages;
using DotNetFramework.CAP.Models;
using DotNetFramework.CAP.Monitoring;
using DotNetFramework.CAP.Persistence;
using DotNetFramework.CAP.Serialization;

namespace DotNetFramework.CAP.InMemoryStorage
{
    internal class InMemoryStorage : IStorageConnection
    {
        private readonly CapOptions _capOptions;
        //private readonly ISerializer _serializer;

        public InMemoryStorage(CapOptions capOptions/*, ISerializer serializer*/)
        {
            _capOptions = capOptions;
            //_serializer = serializer;
        }

        public static ConcurrentDictionary<string, CapPublishedMessage> PublishedMessages { get; } = new ConcurrentDictionary<string, CapPublishedMessage>();

        public static ConcurrentDictionary<string, CapReceivedMessage> ReceivedMessages { get; } = new ConcurrentDictionary<string, CapReceivedMessage>();



        //public MediumMessage StoreMessage(string name, Message content, object? dbTransaction = null)
        //{
        //    var message = new MediumMessage
        //    {
        //        DbId = content.GetId(),
        //        Origin = content,
        //        Content = _serializer.Serialize(content),
        //        Added = DateTime.Now,
        //        ExpiresAt = null,
        //        Retries = 0
        //    };

        //    PublishedMessages[message.DbId] = new MemoryMessage
        //    {
        //        DbId = message.DbId,
        //        Name = name,
        //        Content = message.Content,
        //        Retries = message.Retries,
        //        Added = message.Added,
        //        ExpiresAt = message.ExpiresAt,
        //        StatusName = StatusName.Scheduled
        //    };

        //    return message;
        //}

        public void StoreReceivedExceptionMessage(string name, string group, string content)
        {
            var id = SnowflakeId.Default().NextId();//.ToString();

            ReceivedMessages[id.ToString()] = new CapReceivedMessage
            {
                Id = id,
                Group = group,
                //Origin = null!,
                Name = name,
                Content = content,
                Retries = _capOptions.FailedRetryCount,// _capOptions.Value.FailedRetryCount,
                Added = DateTime.Now,
                ExpiresAt = DateTime.Now.AddSeconds(30),//_capOptions.Value.FailedMessageExpiredAfter),
                StatusName = StatusName.Failed
            };
        }

        public void StoreReceivedMessage(CapReceivedMessage message)
        {
            ReceivedMessages[message.Id.ToString()] = message;
            //var mdMessage = new MediumMessage
            //{
            //    DbId = SnowflakeId.Default().NextId().ToString(),
            //    Origin = message. //message,
            //    Added = DateTime.Now,
            //    ExpiresAt = null,
            //    Retries = 0
            //};

            //ReceivedMessages[message.Id] = new MemoryMessage
            //{
            //    DbId = mdMessage.DbId,
            //    Origin = mdMessage.Origin,
            //    Group = group,
            //    Name = name,
            //    Content = _serializer.Serialize(mdMessage.Origin),
            //    Retries = mdMessage.Retries,
            //    Added = mdMessage.Added,
            //    ExpiresAt = mdMessage.ExpiresAt,
            //    StatusName = StatusName.Scheduled
            //};

            //return mdMessage;
        }

        public Task<int> DeleteExpiresAsync(string table, DateTime timeout, int batchCount = 1000, CancellationToken token = default)
        {

            var removed = 0;
            if (table == nameof(PublishedMessages))
            {
                var ids = PublishedMessages.Values
                    .Where(x => x.ExpiresAt < timeout)
                    .Select(x => x.Id)
                    .Take(batchCount);

                foreach (var id in ids)
                {
                    if (PublishedMessages.TryRemove(id.ToString(), out _))
                    {
                        removed++;
                    }
                }
            }
            else
            {
                var ids = ReceivedMessages.Values
                    .Where(x => x.ExpiresAt < timeout)
                    .Select(x => x.Id)
                    .Take(batchCount);

                foreach (var id in ids)
                {
                    if (ReceivedMessages.TryRemove(id.ToString(), out _))
                    {
                        removed++;
                    }
                }
            }

            return Task.FromResult(removed);
        }

        public Task<IEnumerable<CapPublishedMessage>> GetPublishedMessagesOfNeedRetry()
        {
            IEnumerable<CapPublishedMessage> result = PublishedMessages.Values
                .Where(x => x.Retries < _capOptions.FailedRetryCount
                            && x.Added < DateTime.Now.AddSeconds(-10)
                            && (x.StatusName == StatusName.Scheduled || x.StatusName == StatusName.Failed))
                .Take(200)
                .Select(x => (CapPublishedMessage)x).ToList();

            //foreach (var message in result)
            //{
            //    message.Origin = message.Content;// _serializer.Deserialize(message.Content)!;
            //}

            return Task.FromResult(result);
        }

        public Task<IEnumerable<CapReceivedMessage>> GetReceivedMessagesOfNeedRetry()
        {
            IEnumerable<CapReceivedMessage> result = ReceivedMessages.Values
                .Where(x => x.Retries < _capOptions.FailedRetryCount
                            && x.Added < DateTime.Now.AddSeconds(-10)
                            && (x.StatusName == StatusName.Scheduled || x.StatusName == StatusName.Failed))
                .Take(200)
                .Select(x => (CapReceivedMessage)x).ToList();

            //foreach (var message in result)
            //{
            //    message.Origin = _serializer.Deserialize(message.Content)!;
            //}

            return Task.FromResult(result);
        }

        public IMonitoringApi GetMonitoringApi()
        {
            return new InMemoryMonitoringApi();
        }

        public Task<CapPublishedMessage> GetPublishedMessageAsync(long id)
        {
            if (PublishedMessages.ContainsKey(id.ToString()))
                return Task.FromResult(PublishedMessages[id.ToString()]);
            return Task.FromResult(new CapPublishedMessage() { Content = null, StatusName = "ng" });
        }

        public Task<CapReceivedMessage> GetReceivedMessageAsync(long id)
        {
            if (ReceivedMessages.ContainsKey(id.ToString()))
                return Task.FromResult(ReceivedMessages[id.ToString()]);
            return Task.FromResult(new CapReceivedMessage() { Content = null, StatusName = "ng" });
        }


        public IStorageTransaction CreateTransaction()
        {
            return null;
        }

        public bool ChangePublishedState(long messageId, string state)
        {
            //PublishedMessages[messageId].StatusName = state;
            //PublishedMessages[messageId].ExpiresAt = message.ExpiresAt;
            PublishedMessages[messageId.ToString()].Content = state;// _serializer.Serialize(message.Origin);
            return true;
        }

        public bool ChangeReceivedState(long messageId, string state)
        {
            //ReceivedMessages[messageId].StatusName = state;
            //ReceivedMessages[messageId].ExpiresAt = message.ExpiresAt;
            ReceivedMessages[messageId.ToString()].Content = state;// _serializer.Serialize(message.Origin);
            return true;
        }
    }
}
